/*
 * Copyright 2008 Nordic Consulting & Development Company
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package pl.ncdc.web.login.client;

import pl.ncdc.web.login.client.i18n.LoginConstants;
import pl.ncdc.web.login.client.service.AuthenticationResult;
import pl.ncdc.web.login.client.service.AuthenticationService;
import pl.ncdc.web.login.client.service.AuthenticationServiceAsync;
import pl.ncdc.web.login.client.service.UserService;
import pl.ncdc.web.login.client.service.UserServiceAsync;
import pl.ncdc.web.login.client.ui.CredentialsListenerAdapter;
import pl.ncdc.web.login.client.ui.DefaultUserWidgetFactory;
import pl.ncdc.web.login.client.ui.NotificationAreaWidget;
import pl.ncdc.web.login.client.ui.UserCredentialsWidget;
import pl.ncdc.web.login.client.ui.UserListWidget;
import pl.ncdc.web.login.client.ui.UserSelectionListener;
import pl.ncdc.web.login.client.ui.UidSuggestOracle;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.rpc.ServiceDefTarget;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.SuggestOracle;

/**
 * Login application entry point.
 * <p>
 * Created on Apr 15, 2008
 *
 * @author hshsce
 * @version $Id: LoginApp.java 69 2008-05-05 09:41:07Z hshsce $
 */
public class LoginApp implements EntryPoint {

	public static final String REMEMBER_ME_CHECK_BOX_ID = "ncdc-web-login-RememberMeCheckBox";

	public static final String PROPERTIES_DICTIONARY = "LoginProperties";

	public static final String REMEMBER_ME_PROPERTY = "rememberMe";

	public static final String REDIRECT_URL_PROPERTY = "redirectUrl";

	public static final String IMAGE_PATH_PROPERTY = "imagePath";

	public static final String IMAGE_EXTENSION_PROPERTY = "imageExtension";

	private static final LoginConstants CONSTANTS = (LoginConstants) GWT.create(LoginConstants.class);


	private NotificationAreaWidget m_notificationArea;

	private CheckBox m_rememberMeCheckBox;

	private UserListWidget m_userList;

	private UserCredentialsWidget m_userCredentials;


	/*
	 * services
	 */
	private AuthenticationServiceAsync m_authService;

	private UserServiceAsync m_userService;


	/*
	 * properties
	 */
	private boolean m_rememberMeDefault;

	private String m_redirectUrl;

	private String m_imagePath;

	private String m_imageExtension;

	/**
	 * This is the entry point method.
	 */
	public void onModuleLoad() {
		configure();
		initNotificationArea();
		initRememberMeCheckBox();

		final RootPanel panel = RootPanel.get(UserCredentialsWidget.WIDGET_ID);
		if (panel == null) {
			Window.alert("No required HTML element wiht id: " + UserCredentialsWidget.WIDGET_ID + ", unable to initialize application");
		} else {
			m_userService = (UserServiceAsync) getService(GWT.create(UserService.class), "UserService");
			m_authService = (AuthenticationServiceAsync) getService(GWT.create(AuthenticationService.class), "AuthenticationService");
			initUserCredentialsWidget();
			initUserListWidget();
			connectListeners();
			panel.add(m_userCredentials);
			m_userCredentials.setFocus(true);
		}
	}

	private void configure() {
		final ConfigurationHandler c = new ConfigurationHandler(PROPERTIES_DICTIONARY);

		final String rmProp = c.getProperty(REMEMBER_ME_PROPERTY, "false");
		m_rememberMeDefault = rmProp.equalsIgnoreCase("true");

		final String redirectUrl = c.getProperty(REDIRECT_URL_PROPERTY, null);
		if (redirectUrl != null) {
			if (redirectUrl.startsWith("http://")) {
				m_redirectUrl = redirectUrl;
			} else {
				m_redirectUrl = GWT.getModuleBaseURL() + redirectUrl;
			}
		} else {
			//assuming we should redirect to the module base
			m_redirectUrl = GWT.getModuleBaseURL();
		}

		m_imagePath = c.getProperty(IMAGE_PATH_PROPERTY, "images/users");
		m_imageExtension = c.getProperty(IMAGE_EXTENSION_PROPERTY, "jpg");
	}

	private void initNotificationArea() {
		final RootPanel panel = RootPanel.get(NotificationAreaWidget.WIDGET_ID);
		if (panel != null) {
			m_notificationArea = new NotificationAreaWidget();
			panel.add(m_notificationArea);
		}
	}

	private void initRememberMeCheckBox() {
		final RootPanel panel = RootPanel.get(REMEMBER_ME_CHECK_BOX_ID);
		if (panel != null) {
			m_rememberMeCheckBox = new CheckBox(CONSTANTS.rememberMe());
			panel.add(m_rememberMeCheckBox);			
		}
	}

	private void initUserCredentialsWidget() {
		final SuggestOracle oracle = new UidSuggestOracle(m_userService);
		m_userCredentials = new UserCredentialsWidget(oracle);
		m_userCredentials.addCredentialsListener(new CredentialsListenerAdapter() {
			public void onUserActivity() {
				if (m_notificationArea != null) {
					m_notificationArea.clear();
				}
			}
			public void onCredentialsProvided(final String uid, final String password) {
				prepareAuthentication(uid);
				authenticate(uid, password, getRememberMeOption());
			}
		});
	}

	private void initUserListWidget() {
		final RootPanel userListPanel = RootPanel.get(UserListWidget.WIDGET_ID);
		m_userList = new UserListWidget(new DefaultUserWidgetFactory(m_imagePath, m_imageExtension));
		if (userListPanel != null) {
			userListPanel.add(m_userList);
			m_userService.list(new AsyncCallback() {
				public void onSuccess(final Object result) {
					m_userList.setUsers((User[]) result);
				}
				public void onFailure(final Throwable caught) {
					Window.alert(CONSTANTS.serverCommunicationError());
				}
			});
		}
	}

	private void authenticate(final String uid, final String password, final boolean rememberMe) {
		m_authService.login(uid, password, rememberMe, new AsyncCallback() {
			public void onSuccess(final Object result) {
				handleAuthenticationResult((AuthenticationResult) result);
			}
			public void onFailure(final Throwable caught) {
				Window.alert(CONSTANTS.serverCommunicationError());
				resetWidgets();
			}
		});		
	}

	private void handleAuthenticationResult(final AuthenticationResult result) {
		final int code = result.getCode();
		switch (code) {
		case AuthenticationResult.OK:
			Window.open(m_redirectUrl, "_self", "");
			break;
		case AuthenticationResult.INVALID_UID:
			handleError(CONSTANTS.invalidUsername());
			break;
		case AuthenticationResult.INVALID_PASSWORD:
			handleError(CONSTANTS.invalidPassword());
			break;
		default:
			throw new AssertionError("Unknown AuthenticationResult code: " + code);
		}
	}

	private void prepareAuthentication(final String uid) {
		if (m_notificationArea != null) {
			m_notificationArea.setBusy();
		}
		if (m_rememberMeCheckBox != null) {
			m_rememberMeCheckBox.setEnabled(false);
		}
		if (m_userList != null) {
			m_userList.setEnabled(false);
			m_userList.selectUser(uid);
		}
		m_userCredentials.setEnabled(false);
	}

	private void resetWidgets() {
		if (m_notificationArea != null) {
			m_notificationArea.clear();
		}
		if (m_rememberMeCheckBox != null) {
			m_rememberMeCheckBox.setEnabled(true);
		}
		m_userCredentials.reset();
		m_userCredentials.setEnabled(true);
	}

	private void handleError(final String message) {
		if (m_notificationArea != null) {
			m_notificationArea.setMessage(message);
		}
		if (m_rememberMeCheckBox != null) {
			m_rememberMeCheckBox.setEnabled(true);
		}
		if (m_userList != null) {
			m_userList.setEnabled(true);
		}
		m_userCredentials.showUidPrompt();
		m_userCredentials.setEnabled(true);
		m_userCredentials.setFocus(true);
	}

	private boolean getRememberMeOption() {
		final boolean result;
		if (m_rememberMeCheckBox != null) {
			result = m_rememberMeCheckBox.isChecked();
		} else {
			result = m_rememberMeDefault;
		}
		return result;
	}

	private Object getService(final Object service, final String serviceUri) {
		final ServiceDefTarget endpoint = (ServiceDefTarget) service;
		final String moduleRelativeURL = GWT.getModuleBaseURL() + "service/" + serviceUri;
		endpoint.setServiceEntryPoint(moduleRelativeURL);
		return service;
	}

	private void connectListeners() {
		if (m_userList != null) {
			m_userList.addUserSelectionListener(new UserSelectionListener() {
				public void onUserSelected(final String uid) {
					if (m_notificationArea != null) {
						m_notificationArea.clear();
					}
					m_userCredentials.setUid(uid);
				}
			});			
		}
		m_userCredentials.addCredentialsListener(new CredentialsListenerAdapter() {
			public void onUidChangeAttempt() {
				if (m_notificationArea != null) {
					m_notificationArea.clear();
				}				
			}
			public void onUidProvided(final String uid) {
				m_userList.selectUser(uid);
			}
		});
	}

}
